import core from "@/core/"
import extname from "./extname"
import action from "./action"
import excel from "./excel"
import download from "./download"

/**
 * 文件管理，默认是在common云服务器中。并且完全开放权限，可以在前端增删改查
 * 也可以在上传时指定文件管理的云服务数据库信息
 * 初始化时默认判断当前云服务空间是否有file表，如果没有则用common空间
 */
const DefaultTableInfo = {space: 'common', name: 'file'}
// 默认文件最大的大小
const DefaultMaxSize = 10

/**
 * 默认的限制信息
 * 每次使用时需复制出来
 */
const DefaultCeilingSize = {
	continue: true, // 超过限制，是否允许继续上传
	top: 0.1, // 上限
	bottom: 0.1, // 下限
	width: 0, // 要求的宽度
	height: 0, // 要求的高度
	duration: 0, // 时长上限
	maxSize: DefaultMaxSize, // 最大尺寸，默认10M
}

// 内容限制的信息的默认处理
const handleCeilingSize = (ceilingSize) => {
	if (ceilingSize) {
		Object.keys(DefaultCeilingSize).forEach(key => {
			if (ceilingSize[key] === undefined) {
				ceilingSize[key] = DefaultCeilingSize[key]
			}
		})
		return ceilingSize
	}
	return DefaultCeilingSize
}

// 文件大小的写法
const fileSize = (size) => {
	if (size) {
		const value = parseFloat(size)
		if (value > 1024 * 1024 * 1024) {
			const res = value / (1024 * 1024 * 1024)
			return res.toFixed(3) + 'G'
		} else if (value > 1024 * 1024) {
			const res = value / (1024 * 1024)
			return res.toFixed(3) + 'M'
		} else if (value > 1024) {
			const res = value / 1024
			return res.toFixed(3) + 'kb'
		}
		return value + 'b'
	}
	return ''
}

// 删除文件信息
const remove = (params, tableInfo, isDelete = false, loading = false) => {
	if (!tableInfo) {
		tableInfo = {...DefaultTableInfo}
	}
	return core.request.remove(tableInfo, params, isDelete, loading)
}

// 修改文件信息。一般调用用来修改文件的封面图等。大部分数据不需要调用修改
const update = (params, data, tableInfo, loading = false) => {
	if (!tableInfo) {
		tableInfo = {...DefaultTableInfo}
	}
	return core.request.alert(tableInfo, params, data, loading)
}

/**
 * 获取文件信息
 * 一般是通过key值(url或者md5)来获取对应value的文件详情信息
 * value：url或者md5的字符串或者数组
 */
const get = (value, key = 'url', tableInfo, loading = false) => {
	if (!tableInfo) {
		tableInfo = {...DefaultTableInfo}
		// 优先读取url中的space
		if (key === 'url') {
			const arr = value.split('/').pop().split('-')
			if (arr.length >= 3 && core.request.basedata[arr[1]]) {
				tableInfo.space = arr[1]
			}
		}
	}
	return new Promise((resolve, reject) => {
		if (typeof(value) === 'string') {
			value = value.split(',')
		}
		if (Array.isArray(value) && value.length > 0) {
			const params = {}
			params[key] = {filterType: 'select', filter: value}
			core.request.get(tableInfo, params, undefined, loading).then(res => {
				resolve(res)
			}).catch(reject)
		} else {
			// 没有参数，返回空数组
			resolve({data: []})
		}
	})
}

// 上传单个文件到其它服务器
const exterior = (file, filePath, fileType, md5, fileMediatype, ceilingSize, uploadParams, process) => {
	return new Promise((resolve, reject) => {
		// base64直接POST上传
		// return action.getBase64(file).then(base64 => {
		// 	var extName = action.getExtension(file, path)
		// 	core.current.fetch('upload_base64', {base64, extName}, true).then(res => {
		// 		resolve({url: res.data})
		// 	}).catch(reject)
		// })
		// 常规上传
		const {url, header, handle} = uploadParams
		uni.uploadFile({
			url, header,
			fileType,
			file, // 仅h5支持。其它平台需要filePath或者files
			filePath, // 非H5需要，或者使用files
			// #ifndef H5
			name: md5, // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
			// #endif
			success(res) {
				console.log(res)
				if (res.statusCode === 200) {
					resolve(handle ? handle(res.data) : res.data)
				} else {
					reject(res)
				}
			},
			fail(err) {
				reject(err)
			}
		})
	})
}

/**
 * 上传单个文件到云服务空间
 * 	先通过md5查询是否已存在，存在直接返回文件信息
 * 	不存在，则先上传。上传路径是七牛云：https://qiniu.capfer.cn
 * 	上传完成，再给tableInfo新增数据以记录
 */
const interior = (file, filePath, fileType, md5, fileMediatype, ceilingSize, tableInfo, process) => {
	return new Promise((resolve, reject) => {
		// 1，查询该md5对应的文件是否已存在
		get(md5, 'md5', tableInfo).then(res => {
			if (res.data.length > 0) {
				resolve(res.data[0])
			} else {
				// 记录到文件数据库的文件数据
				const extension = action.getExtension(file, filePath)
				const fileType = file.type || extension || '未知'
				const fileData = {
					md5, // MD5值
					url: '', // 文件链接
					type: fileMediatype, // 文件类型。同fileMediatype
					used: 0, // 文件用途 0：默认；1：素材
					source: 'default', // 文件来源。ai：AI作图；copy：网络粘贴；default：默认，本地上传
					name: action.getFileName(file, filePath), // 文件名。不含后缀
					extension, // 文件名后缀
					size: file.size, // 文件大小。单位b
					width: '', // 文件宽度。图片视频特有
					height: '', // 文件高度。图片视频特有
					scale: '', // 宽高比例。图片视频特有
					duration: '', // 时长。音频视频特有
					/**
					 * 存储于云的名称
					 * 名称(md5)-云服务空间名-文件类型。后缀名(如果没有后缀名，就没有.)
					 * 云服务空间名：是为了通过url判断向哪个云服务空间获取文件信息
					 * 文件类型：是为了通过url判断文件显示的类型
					 * 后缀名：是为了通过url判断文件真实类型
					 * 
					 * 腾讯云cloudPath不能重复，否则覆盖。如果没有权限则上传失败
					 * 目前已知阿里云中，返回的url看起来和cloudPath并没有关系
					 */
					cloudPath: `${md5}-${tableInfo.space}-${fileMediatype}${extension ? ('.' + extension) : ''}`,
					fileType, // 文件类型
					target: [core.info.target.code],
					partya: [core.info.target.partya],
				}
				// 获取图片宽高
				uni.getImageInfo({
					src: filePath,
					complete(res) {
						if (res.errMsg === 'getImageInfo:ok') {
							fileData.width = res.width
							fileData.height = res.height
						}
						// 获取音视频时长和宽高
						uni.getVideoInfo({ // 该方法正常文件都会走成功getVideoInfo:ok，但是只有size值
							src: filePath,
							complete(res) {
								if (res.errMsg === 'getVideoInfo:ok') {
									if (res.width) {
										fileData.width = res.width
									}
									if (res.height) {
										fileData.height = res.height
									}
									if (res.duration) {
										fileData.duration = res.duration
									}
								}
								if (fileData.width && fileData.height) {
									// 宽高比例
									fileData.scale = core.synctool.number.round(fileData.width / fileData.height)
								}
								
								// 如果有限定宽高的情况
								action.handleSizeModal(ceilingSize, fileData, (bool) => {
									if (bool) {
										// 是否支持七牛云
										const qiniu = core.request.api.qiniu
										if (qiniu) {
											core.request.fetchData('qiniu','getUploadFileOptions', fileData).then(res => {
												// 上传到七牛云
												handleUpload(filePath, fileData, tableInfo, res.data, process, resolve, reject)
											})
										} else {
											// 直接上传
											handleUpload(filePath, fileData, tableInfo, {
												uploadFileOptions: {cloudPath: fileData.cloudPath}
											}, process, resolve, reject)
										}
									} else {
										// 取消了上传
										core.showToast('取消了当前上传', () => {
											console.warn('取消了当前上传')
											reject('取消')
										})
									}
								})
							}
						})
					}
				})
			}
		})
	})
}

/**
 * 处理文件上传
 * 
 * 上传成功之后，要更新记录文件信息
 */
const handleUpload = (filePath, fileData, tableInfo, uploadFileOptionsRes, process, resolve, reject) => {
	// 七牛云用uni.uploadFile；正常上传用uniCloud.uploadFile
	var uploadTask, errorText = '没有云服务空间信息'
	if (uploadFileOptionsRes.fileURL) {
		uploadTask = uni
	} else {
		const {cloud, text} = core.request.getCloud(tableInfo)
		uploadTask = cloud
		errorText = text || errorText
	}
	if (uploadTask) {
		uploadTask = uploadTask.uploadFile({
			...uploadFileOptionsRes.uploadFileOptions, // 上传文件所需参数
			filePath: filePath, // 本地文件路径
			onUploadProgress: (progressEvent) => {
				console.log(progressEvent);
				const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
				console.log(percentCompleted)
				process && process(percentCompleted)
			},
			success: (res) => {
				// 处理上传结果
				core.synctool.handleUploadResult(fileData, res, uploadFileOptionsRes)
				// 写入文件列表
				core.request.add(tableInfo, fileData).then(res => {
					resolve(fileData)
				}).catch(err => {
					resolve(fileData)
				})
			},
			fail: (err) => {
				console.log('上传失败', err)
				core.showToast('上传失败')
				reject(err)
			}
		})
		// 监听上传进度 - uni.uploadFile才有；uniCloud.uploadFile没有
		uploadTask && uploadTask.onProgressUpdate && uploadTask.onProgressUpdate((res) => {
			process && process(res.progress)
		})
	} else {
		console.error(errorText)
		reject(errorText)
	}
}

module.exports = {
	excel, // 未测试通过。241128
	download, // H5测试通过。250310
	// 初始化方法，目前仅初始化默认的云服务空间数据
	initial() {
		const {basedata} = core.request
		const {current, CommonSpaceName} = basedata
		if (basedata[current] && basedata[current].fileDb) {
			// 优先使用本云服务空间的文件数据库
			DefaultTableInfo.space = current
			DefaultTableInfo.name = basedata[current].fileDb
		} else if (basedata[CommonSpaceName].fileDb) {
			// 其次使用公共云服务空间的文件数据库
			DefaultTableInfo.space = CommonSpaceName
			DefaultTableInfo.name = basedata[CommonSpaceName].fileDb
		} else {
			// 最后使用其它云服务空间的文件数据库
			for (let key in basedata) {
				if (key !== current && key !== CommonSpaceName) {
					if (basedata[key] && basedata[key].fileDb) {
						DefaultTableInfo.space = key
						DefaultTableInfo.name = basedata[key].fileDb
						break
					}
				}
			}
		}
	},
	// 文件的公共方法
	action,
	// 文件后缀名相关
	extname, 
	// 文件大小的写法
	fileSize,
	/**
	 * 文件上传
	 * 先通过md5查询是否已存在，存在直接返回文件信息
	 * 不存在，则先上传。上传路径是七牛云：https://qiniu.capfer.cn（这是common云服务空间绑定的七牛云）
	 * 上传完成，再给tableInfo新增数据以记录
	 * 
	 * data总包含
	 * 	res：选择文件的原始信息
	 * 	fileMediatype：选择文件的类型
	 * 	ceilingSize：对文件的限制信息。包含
	  		continue: true, // 超过限制，是否允许继续上传
			top: 0.1, // 宽高时长的上限
			bottom: 0.1, // 宽高的下限
			width: 0, // 要求的宽度
			height: 0, // 要求的高度
			duration: 0, // 时长上限
			maxSize: DefaultMaxSize, // 最大尺寸，默认10M
	 * 	tableInfo：文件管理的云服务空间
	 * 	uploadParams：其它自定义的上传参数。如果存在，表示上传到指定的服务器上。不存在，表示上传到云服务空间中，默认是tableInfo
	 * 	handle：原始文件信息处理完成后的回调
	 * 
	 * process：上传进度回调。第一个参数是进度值，第二个参数是文件的path，用以确定到底是哪个文件上传的进度
	 */
	upload(data, process) {
		return new Promise((resolve, reject) => {
			if (data && data.res) {
				var {res, ceilingSize, tableInfo, uploadParams, fileMediatype, handle} = data
				ceilingSize = handleCeilingSize(ceilingSize)
				if (!tableInfo) {
					tableInfo = {...DefaultTableInfo}
				}
				// 先对已选择的文件做处理
				action.handleSelectedFiles(res, ceilingSize).then(data => {
					/**
					 * data的内容：
					 * 	...res：选择文件的原始信息。删除了tempFile和tempFilePath
					 * 	tempFiles：files
					 * 	tempFilePaths：paths
					 * 	fileTypes：每一个文件的真实文件类型
					 * 	md5s：每一个文件的MD5值
					 * 	count：满足条件的文件数量
					 */
					
					// 满足基础上传文件的信息回调，用以初步显示
					handle && handle(data)
					
					// 有uploadParams，表示上传到对应的服务器中；无uploadParams，表示上传到tableInfo的云服务空间中
					const updateAction = uploadParams ? exterior : interior
					/**
					 * 上传文件。原理是一个一个处理
					 * 
					 */
					const {tempFiles, tempFilePaths, fileTypes, md5s, count} = data
					// 记录上传成功的结果。上传失败的角标
					const resArr = [], failCount = []
					const cycle = (index = 0) => {
						if (index < count) {
							uni.showLoading({
								title: count === 1 ? '正在上传中' : `正在上传第${index + 1}个`
							})
							// 在上传成功前，该path可当做url使用
							const path = tempFilePaths[index]
							const fileType = fileTypes && fileTypes.length > index ? fileTypes[index] : undefined
							const md5 = md5s && md5s.length > index ? md5s[index] : undefined
							updateAction(tempFiles[index], path, fileType, md5, fileMediatype, ceilingSize, uploadParams || tableInfo, (value) => {
								process && process(index, value, path)
							}).then(res => {
								uni.hideLoading()
								resArr.push({path, data: res})
								cycle(index + 1)
							}).catch(err => {
								uni.hideLoading()
								resArr.push({path})
								failCount.push(index)
								cycle(index + 1)
							})
						} else {
							var msg = '上传完成'
							if (failCount.length > 0) {
								if (failCount.length < count) {
									msg = `上传完成：其中成功${resArr}个；失败${failCount}个`
								} else {
									msg = '上传失败'
								}
							}
							console.log(msg)
							core.showToast(msg)
							resolve(resArr)
						}
					}
					cycle()
				}).catch(reject)
			} else {
				reject('上传参数不存在或者不正确')
			}
		})
	},
	
	// 删除文件信息
	remove,
	
	// 修改文件信息。一般调用用来修改文件的封面图等。大部分数据不需要调用修改
	update,
	
	/**
	 * 获取文件信息
	 * 一般是通过key值(url或者md5)来获取对应value的文件详情信息
	 * value：url或者md5的字符串或者数组
	 */
	get,
	
	/**
	 * 根据数组内容导出数据
	 */
	export({
		isTemplate, // 是否为下载模版模式
		show = true, // 是否显示下载模式
		modalTitle = '操作提示', // 对话框标题的标题
		buttonTitle, // 导出按钮的标题
		fileName, // 导出文件的名称
		content, // 对话框的中间内容
		title, // 数据的标题
		type = 'xlsx', // 导出文件类型 xls或者csv
		explan, // Array 数据说明。不建议传footer，footer会自动生成
		
		data, // Array 需要导出的数据 - 固定数据
		fetch, // Function 需要导出的数据 - Promise返回数据。data不存在时生效
		fields, // Object 要导出的JSON对象中的字段，如果没有提供，JSON中的所有属性将被导出
		// name, // 导出的文件名称。会自动拼接后缀type
		footer, // String, Array 表格的页脚。可以是字符串（一个页脚）或字符串数组（多个页脚）
		worksheet, // String 工作表选项卡的名称
		defaultValue, // 如果某一行没有字段值时候起作用，当行没有字段值时用作回退
		stringifyLongNum = true, // 字符串化长数字和小数（解决数字精度损失的问题），默认值：false
		escapeCsv = true, // 这将转义CSV值，以修复一些excel数字字段的问题。但这会用=" and "包装所有csv数据，以避免你不得不将这个道具设为假。默认值:true
		
		beforeGenerate,
		beforeFinish,
		
		confirmText, // 确认的文字
		confirmColor, // 确认的颜色
		confirmAction, // 确认的事件
	} = {}) {
		if (!fileName) {
			const prefix = isTemplate ? '模板' : '数据'
			fileName = `${title}${prefix}文件`
		}
		if (!fetch && !data) {
			data = [{}]
		}
		if (!footer) {
			footer = []
			if (explan && explan.length > 0) {
				footer.push(`数据说明：<br/>` + explan.join('；<br/>') + '。')
			}
			var lastStr = `操作人：${core.user.info.name || core.user.info.phone || core.user.info._id || '未登录用户'}；操作时间：${core.time.getString()}。`
			if (isTemplate) {
				lastStr += '该第3、4行可以删除。'
			}
			footer.push(lastStr)
		}
		if (!content) {
			content = `是否立即${isTemplate ? '下载模板' : `导出${data ? data.length : ''}${data ? '条' : ''}数据`}`
		}
		const excel = show ? {
			type, show: true, // 是否显示
			title: buttonTitle || (isTemplate ? '下载模板' : '立即导出'), // 导出按钮的名称
			name: fileName || `${title}的${isTemplate ? '导入模板' : '导出数据'}`, // 导出文件的名称
			options: {
				name: fileName, // 导出的文件名称。会自动拼接后缀type
				data, fetch, fields, footer, worksheet, defaultValue, stringifyLongNum, escapeCsv
			},
			beforeGenerate, // Function 在生成/获取数据之前调用方法，例如:显示加载进度
			beforeFinish, // Function 在下载框弹出之前调用方法的回调，例如:隐藏加载进度
		} : undefined
		core.showModal({title: modalTitle, content, excel, confirmText, confirmColor, success(res) {
			if (res.confirm) {
				confirmAction && confirmAction()
			}
		}})
	}
}